#ifndef BOUNDARYCONDITION_HPP
#define BOUNDARYCONDITION_HPP

#include "meshTypes_fwd.hpp"
#include "solverBaseTypes_fwd.hpp"
#include <experimental/memory>

namespace hms
{

namespace boundaryCondition {
	enum class Type {
		staticValue,
		dynamicValue,
		zeroGradient,
		slip_velocity,
		freeOutflow_velocity,
		freeOutflow_waterdepth,
		constantWaterDepth_velocity,
		orthogonalDischarge_velocity,
		orthogonalDischarge_waterdepth,
		orthogonalFlowVelocity_velocity,
		orthogonalFlowVelocity_waterdepth,
		weir_waterdepth,
		weir_velocity,
		weir_bottomElevation,
		nTypes
	};
} // namespace boundaryCondition

using std::experimental::observer_ptr;
using std::experimental::make_observer;

template<typename _MeshType>
class BoundaryCondition
{
public:
	using MeshType = _MeshType;
	using Type = boundaryCondition::Type;
protected:
	mutable observer_ptr<Field<MeshType>> m_field {nullptr};
	observer_ptr<const BoundaryPatch> m_patch {nullptr};
	Type m_type;
	bool m_hasType {false};

	BoundaryCondition() = default;
	BoundaryCondition(
		Type t,
		Field<MeshType>& f,
		const BoundaryPatch& p
	) :
		m_type {t},
		m_hasType {true}
	{
		field(f);
		patch(p);
	}

	auto field() const -> Field<MeshType>& {
		assert( m_field );
		return *m_field;
	}

public:
	virtual ~BoundaryCondition() = default;

	virtual void update() const = 0;

	Type type() const {
		assert( m_hasType );
		return m_type;
	}

	void field( Field<MeshType>& f ){
		m_field = make_observer<Field<MeshType>>( &f );
	}

	auto patch() const -> const BoundaryPatch& {
		assert( m_patch );
		return *m_patch;
	}

	void patch( const BoundaryPatch& p ){
		m_patch = make_observer<const BoundaryPatch>( &p );
	}
};

} // end namespace hms


#endif